﻿using Microsoft.Extensions.Caching.Memory;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Wechat_PublicNumber.Entity;
using Wechat_PublicNumber.Model;
using Wechat_PublicNumber.SettingModel;

namespace Wechat_PublicNumber.Common
{
    /// <summary>
    /// 三方接口调用
    /// </summary>
    [Injection(DIPattern.Singleton)]
    public class CommonHttpInvoke
    {
        [Autowired]
        private HttpClinetHelper _clinetHelper;

        [Autowired]
        private ILog _logger;

        [Autowired]
        private CommonHttpSetting _config;

        [Autowired]
        private IMemoryCache _cache;

        [Autowired]
        private IHttpClientFactory _httpClientFactory;

        /// <summary>
        /// Gould 根据经纬度获取位置信息
        /// </summary>
        /// <returns></returns>
        public async Task<GouldAddressRespone> GetAddressByLongitude_Latitude(string longitude, string latitude)
        {
            //经纬度获取地址
            var responeData = await _clinetHelper.JsonGet<GouldAddressRespone>($"{_config.GouldBaseUrl}/geocode/regeo?key={_config.GouldKey}&location={longitude},{latitude}&extensions=base&batch=false");
            if (responeData.status == 0)
                return null;

            return responeData;
        }

        /// <summary>
        /// Gould 根据区域编码获取天气
        /// </summary>
        /// <param name="cityCode"></param>
        /// <param name="type">base:返回实况天气 all:返回预报天气</param>
        /// <returns></returns>
        public async Task<GouldWeatherRespone> GetGouldWeatherInfoByAreaCode(string cityCode, string type = "base")
        {
            //根据区域编码获取天气
            var responeData = await _clinetHelper.JsonGet<GouldWeatherRespone>($"{_config.GouldBaseUrl}/weather/weatherInfo?key={_config.GouldKey}&city={cityCode}&extensions={type}");
            if (responeData.status == 0)
                return null;

            return responeData;
        }

        /// <summary>
        /// TianAPI 根据区域编码获取天气
        /// </summary>
        /// <param name="city"></param>
        /// <returns></returns>
        public async Task<TianWeatherRespone> GetTianWeatherInfoByAreaCode(string city)
        {
            //根据区域编码获取天气
            var responeData = await _clinetHelper.JsonGet<TianWeatherRespone>($"{_config.TianBaseUrl}/tianqi/index?key={_config.TianKey}&city={city}&type=1");
            if (responeData.code != 200)
                return null;

            return responeData;
        }

        /// <summary>
        /// XueQiu 下载雪球的一个股票详情页面
        /// </summary>
        /// <param name="stockCode"></param>
        /// <returns></returns>
        public async Task<string> DownloadXueQiuOneStockPage(string stockCode)
        {
            if (string.IsNullOrEmpty(stockCode))
                throw new Exception("stockCode can not Empty");

            //string acw_sc__v2 = string.Empty;

            //try
            //{
            //    using (HttpRequestMessage requestMessage = new HttpRequestMessage())
            //    {
            //        requestMessage.Method = HttpMethod.Get;
            //        requestMessage.RequestUri = new Uri(_config.XueQiuBaseUrl);
            //        requestMessage.Headers.Add("Accept-Encoding", "gzip, deflate, br");
            //        requestMessage.Headers.Add("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36");
            //        requestMessage.Headers.Add("origin", _config.XueQiuBaseUrl);
            //        requestMessage.Headers.Add("Host", "xueqiu.com");

            //        using (var httpclient = _httpClientFactory.CreateClient("GetStock"))
            //        {
            //            var stock = await httpclient.SendAsync(requestMessage);
            //            var returnString = await stock.Content.ReadAsStringAsync();

            //            string pattern = @"var arg1='([^']+)'";

            //            Match match = Regex.Match(returnString, pattern);
            //            var arg1 = match.Groups[1].Value;

            //            var unsbox = Unsbox(arg1);

            //            acw_sc__v2 = HexXor(unsbox, "3000176000856006061501533003690027800375");
            //        }
            //    }
            //}
            //catch (Exception ex)
            //{
            //    _logger.Error(ex, "阿里逆向加密 acw_sc__v2 Error", "acw_sc__v2");
            //}

            using (var client = _httpClientFactory.CreateClient())
            {
                //if (!string.IsNullOrEmpty(acw_sc__v2))
                //    //添加阿里逆向加密
                //    client.DefaultRequestHeaders.Add("cookie", $"acw_sc__v2={acw_sc__v2};");

                var stockMD5 = await DbStatic.WXDb.Queryable<StockMD5>().FirstAsync();

                using (HttpResponseMessage response = await client.GetAsync($"{_config.XueQiuBaseUrl}/S/{stockCode}?{stockMD5.md5}"))
                {
                    using (HttpContent content = response.Content)
                    {
                        var returnPageData = await content.ReadAsStringAsync();

                        var regexMatch = Regex.Match(returnPageData, "hm.js\\?[0-9a-z]+");
                        var id = regexMatch.Value.Split("?")[1];

                        //设置cookie
                        var allcookie = response.Headers.GetValues("Set-Cookie").Select(s => s.Split(";")[0]).ToList();
                        allcookie.Add($"Hm_lvt_{id}={new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}");
                        allcookie.Add($"Hm_lpvt_{id}={new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}");
                        var cookie = string.Join("; ", allcookie);
                        if (!string.IsNullOrEmpty(cookie))
                            _cache.Set("PushStockJobCookie", cookie, TimeSpan.FromDays(1));
                        return returnPageData;
                    }
                }
            }
        }

        private string Unsbox(string str)
        {
            int[] _0x4b082b = { 0xf, 0x23, 0x1d, 0x18, 0x21, 0x10, 0x1, 0x26, 0xa, 0x9, 0x13, 0x1f, 0x28, 0x1b, 0x16, 0x17, 0x19, 0xd, 0x6, 0xb, 0x27, 0x12, 0x14, 0x8, 0xe, 0x15, 0x20, 0x1a, 0x2, 0x1e, 0x7, 0x4, 0x11, 0x5, 0x3, 0x1c, 0x22, 0x25, 0xc, 0x24 };
            char[] _0x4da0dc = new char[_0x4b082b.Length];

            for (int _0x20a7bf = 0; _0x20a7bf < str.Length; _0x20a7bf++)
            {
                char _0x385ee3 = str[_0x20a7bf];
                for (int _0x217721 = 0; _0x217721 < _0x4b082b.Length; _0x217721++)
                {
                    if (_0x4b082b[_0x217721] == _0x20a7bf + 1)
                    {
                        _0x4da0dc[_0x217721] = _0x385ee3;
                    }
                }
            }

            return new string(_0x4da0dc);
        }

        private string HexXor(string str, string _0x4e08d8)
        {
            string _0x5a5d3b = "";

            for (int _0xe89588 = 0; _0xe89588 < str.Length && _0xe89588 < _0x4e08d8.Length; _0xe89588 += 2)
            {
                int _0x401af1 = Convert.ToInt32(str.Substring(_0xe89588, 2), 16);
                int _0x105f59 = Convert.ToInt32(_0x4e08d8.Substring(_0xe89588, 2), 16);
                int _0x189e2c = _0x401af1 ^ _0x105f59;

                _0x5a5d3b += _0x189e2c.ToString("x2");
            }

            return _0x5a5d3b;
        }

        public async Task<decimal> Getpe_ttm(string stockCode)
        {
            if (string.IsNullOrEmpty(stockCode))
                throw new Exception("stockCode can not Empty");

            var stockString = await XueqiuHttpGet($"{_config.XueQiuStockBaseUrl}/v5/stock/quote.json?symbol={stockCode}&extend=detail");

            var quoteObject = JObject.Parse(stockString);

            var quote = quoteObject["data"]["quote"];

            return decimal.Parse(quote["pe_ttm"].ToString());
        }

        /// <summary>
        /// 股票分时数据
        /// </summary>
        /// <param name="stockCode"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public async Task<GetStockTimeSharingReturn> GetStockTimeSharing(string stockCode)
        {
            if (string.IsNullOrEmpty(stockCode))
                throw new Exception("stockCode can not Empty");

            var stockString = await XueqiuHttpGet($"{_config.XueQiuStockBaseUrl}/v5/stock/chart/minute.json?symbol={stockCode}&period=1d");

            var jobj = JObject.Parse(stockString);

            return JsonConvert.DeserializeObject<GetStockTimeSharingReturn>(jobj["data"].ToString());
        }

        /// <summary>
        /// XueQiu 获取股票实时信息
        /// </summary>
        /// <param name="stockCode"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public async Task<List<GetStockPriceReturn>> GetRealTimeStock(List<string> stockCode)
        {
            try
            {
                if (stockCode == null || stockCode.Count <= 0)
                    throw new Exception("stockCode can not Empty");

                List<GetStockPriceReturn> stockList = new List<GetStockPriceReturn>();

                int onceCount = 8;
                int whileCount = stockCode.Count / onceCount;
                if (stockCode.Count % onceCount > 0) whileCount++;
                for (int i = 0; i < whileCount; i++)
                {
                    var onceAllStockCode = stockCode.Skip(i * onceCount).Take(onceCount);
                    var allStock = await XueqiuHttpGet($"{_config.XueQiuStockBaseUrl}/v5/stock/realtime/quotec.json?symbol={string.Join(',', onceAllStockCode)}");

                    var quoteObject = JObject.Parse(allStock);

                    if (quoteObject["data"] != null)
                    {
                        var data = JsonConvert.DeserializeObject<List<GetStockPriceReturn>>(quoteObject["data"].ToString());
                        data.RemoveAll(s => s.high is null);
                        stockList.AddRange(data);
                    }

                    await Task.Delay(50);
                }

                return stockList;
            }
            catch (Exception ex)
            {

                throw;
            }
        }

        /// <summary>
        /// XueQiu 获取股票实时信息
        /// </summary>
        /// <param name="stockCode"></param>
        /// <returns></returns>
        public async Task<GetStockPriceReturn> GetRealTimeStock(string stockCode)
        {
            return (await GetRealTimeStock(new List<string>() { stockCode }))?.FirstOrDefault();
        }

        /// <summary>
        /// XueQiu 搜索股票信息
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public async Task<List<GetStockInfoReturn>> GetStockInfo(string key)
        {
            List<GetStockInfoReturn> returnData = null;
            if (string.IsNullOrEmpty(key))
                throw new Exception("stockCode can not Empty");

            var returnString = await XueqiuHttpGet($"{_config.XueQiuBaseUrl}/query/v1/suggest_stock.json?q={System.Web.HttpUtility.UrlDecode(key, Encoding.UTF8)}");
            var searchStockList = JObject.Parse(returnString);
            if (searchStockList["data"] != null)
                returnData = JsonConvert.DeserializeObject<List<GetStockInfoReturn>>(searchStockList["data"].ToString());
            if (returnData != null)
                returnData = returnData.Where(s => s.stock_type == 11).ToList();

            return returnData;
        }

        /// <summary>
        /// XueQiu 搜索股票信息
        /// </summary>
        /// <param name="type">sha 上海 sza 深圳</param>
        /// <param name="ind_code"></param>
        /// <param name="page"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public async Task<GetStockDeclineReturn> GetStockDecline(int page, string ind_code = null, string type = null)
        {
            GetStockDeclineReturn returnData = null;

            var url = $"{_config.XueQiuStockBaseUrl}/v5/stock/screener/quote/list.json?page={page}&size=90&order=asc&order_by=symbol&market=CN";

            if (ind_code != null)
                url += $"&ind_code={ind_code}";

            if (type != null)
                url += $"&type={type}";

            var returnString = await XueqiuHttpGet(url);
            var searchStockList = JObject.Parse(returnString);
            if (searchStockList["data"] != null)
                returnData = JsonConvert.DeserializeObject<GetStockDeclineReturn>(searchStockList["data"].ToString());

            return returnData;
        }

        /// <summary>
        /// XueQiu 获取所有行业
        /// </summary>
        /// <param name="category">中国 cn 香港 hk</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public async Task<GetStockIndustryReturn> GetStockIndustry(string category)
        {
            GetStockIndustryReturn returnData = null;

            var returnString = await XueqiuHttpGet($"{_config.XueQiuStockBaseUrl}/v5/stock/screener/industries.json?category={category}");
            var searchStockList = JObject.Parse(returnString);
            if (searchStockList["data"] != null)
                returnData = JsonConvert.DeserializeObject<GetStockIndustryReturn>(searchStockList["data"].ToString());

            return returnData;
        }

        /// <summary>
        /// XueQiu 日K
        /// </summary>
        /// <param name="symbol">股票代码</param>
        /// <param name="count">近几日的K线图</param>
        /// <param name="end">毫秒时间戳，默认是当前时间的，可以自己往前推，比如用昨天的时间戳，count=2，则返回昨天和前天的K线图，用今天的，count=2，则返回今天和昨天的K线图</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public async Task<GetStockDayK_Return> GetStockDayK(string symbol, int count, long end)
        {
            GetStockDayK_Return returnData = null;

            var returnString = await XueqiuHttpGet($"{_config.XueQiuStockBaseUrl}/v5/stock/chart/kline.json?symbol={symbol}&begin={end}&period=day&type=before&count=-{count}&indicator=kline,pe,pb,ps,pcf,market_capital,agt,ggt,balance");
            var searchStockList = JObject.Parse(returnString);
            if (searchStockList["data"] != null)
                returnData = JsonConvert.DeserializeObject<GetStockDayK_Return>(searchStockList["data"].ToString());

            return returnData;
        }

        /// <summary>
        /// 雪球网站Get请求
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        private async Task<string> XueqiuHttpGet(string url)
        {
            //if (!_cache.TryGetValue("PushStockJobCookie", out var cookie))
            //    await DownloadXueQiuOneStockPage("SZ000630");

            using (HttpRequestMessage requestMessage = new HttpRequestMessage())
            {
                requestMessage.Method = HttpMethod.Get;
                requestMessage.RequestUri = new Uri(url);
                requestMessage.Headers.Add("Accept", "*/*");
                requestMessage.Headers.Add("Accept-Encoding", "gzip, deflate, br, zstd");
                requestMessage.Headers.Add("Accept-Language", "zh-CN,zh;q=0.9");
                requestMessage.Headers.Add("Origin", "https://xueqiu.com");
                requestMessage.Headers.Add("Priority", "u=1, i");
                requestMessage.Headers.Add("Referer", $"https://xueqiu.com/S/SZ000630");
                requestMessage.Headers.Add("Sec-Ch-Ua", "\"Not)A;Brand\";v=\"8\", \"Chromium\";v=\"138\", \"Google Chrome\";v=\"138\"");
                requestMessage.Headers.Add("Sec-Ch-Ua-Mobile", "?0");
                requestMessage.Headers.Add("Sec-Ch-Ua-Platform", "\"Windows\"");
                requestMessage.Headers.Add("Sec-Fetch-Dest", "empty");
                requestMessage.Headers.Add("Sec-Fetch-Mode", "cors");
                requestMessage.Headers.Add("Sec-Fetch-Site", "same-site");
                requestMessage.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36");
                requestMessage.Headers.Add("Cookie", "xqat=1");

                using (var httpclient = _httpClientFactory.CreateClient("GetStock"))
                {
                    var stock = await httpclient.SendAsync(requestMessage);
                    var returnString = await stock.Content.ReadAsStringAsync();
                    _logger.Info($"url:{url},returnString:{returnString}", "XueqiuHttpGet");
                    return returnString;
                }
            }
        }
    }

    public class GetStockInfoReturn
    {
        public string code { get; set; }

        public string label { get; set; }

        public string query { get; set; }

        public int stock_type { get; set; }
    }

    public class GetStockDeclineReturn
    {
        public int count { get; set; }

        public List<GetStockDeclineReturn_list> list { get; set; }
    }

    public class GetStockDayK_Return
    {
        public string symbol { get; set; }

        public List<string> column { get; set; }

        public List<List<string>> item { get; set; }
    }

    public class GetStockDeclineReturn_list
    {
        /// <summary>
        /// 股票代码
        /// </summary>
        public string symbol { get; set; }

        /// <summary>
        /// 股票名称
        /// </summary>
        public string name { get; set; }

        /// <summary>
        /// 当前价
        /// </summary>
        public string current { get; set; }

        /// <summary>
        /// 涨跌额
        /// </summary>
        public string chg { get; set; }

        /// <summary>
        /// 涨跌幅
        /// </summary>
        public string percent { get; set; }

        /// <summary>
        /// 年初至今
        /// </summary>
        public string current_year_percent { get; set; }

        /// <summary>
        /// 换手率
        /// </summary>
        public string turnover_rate { get; set; }

        /// <summary>
        /// 市值
        /// </summary>
        public long market_capital { get; set; }
    }

    public class GetStockPriceReturn
    {
        /// <summary>
        /// 股票代码 例：SH600111
        /// </summary>
        public string symbol { get; set; }

        /// <summary>
        /// 当前价格
        /// </summary>
        public decimal current { get; set; }

        /// <summary>
        /// 当前涨跌值
        /// </summary>
        [JsonConverter(typeof(DecimalJsonConverter))]
        public decimal chg { get; set; }

        /// <summary>
        /// 当前涨跌百分比
        /// </summary>
        [JsonConverter(typeof(DecimalJsonConverter))]
        public decimal percent { get; set; }

        public decimal? open { get; set; }

        public decimal? high { get; set; }

        public decimal? low { get; set; }

        public decimal last_close { get; set; }
    }

    public class GetStockIndustryReturn
    {
        public List<GetStockIndustryReturn_Industry> industries { get; set; }
    }

    public class GetStockIndustryReturn_Industry
    {
        public string encode { get; set; }

        public string name { get; set; }

        public string pinyin { get; set; }
    }

    public class GetStockTimeSharingReturn
    {
        public List<GetStockTimeSharingReturn_items> items { get; set; }
    }

    public class GetStockTimeSharingReturn_items
    {
        public decimal current { get; set; }

        public decimal percent { get; set; }

        public long timestamp { get; set; }

        public DateTime time
        {
            get
            {
                return DateTimeOffset.FromUnixTimeMilliseconds(timestamp).LocalDateTime;
            }
        }
    }
}
